package bancosys.tec.utils.date;

import java.util.Calendar;

/**
 * @created Dec 4, 2006
 * @author Gustavo Almeida
 */
public class Timestamp extends java.sql.Timestamp implements Cloneable {
    private transient Calendar calendar;

    /**
     * Cria um timestamp a partir de um calendar.
     * 
     * @param date Calendar.
     */
    public Timestamp(Calendar date) {
        this(date.getTimeInMillis());
    }

    /**
     * Cria um timestamp a partir de um long que representa uma data.
     * 
     * @param date Long que representa uma data.
     */
    public Timestamp(long date) {
        super(date);
        this.getCalendar().setTimeInMillis(date);
        this.updateTime();
    }

    /**
     * Cria um timestamp com a data/hora atual.
     */
    public Timestamp() {
        this(System.currentTimeMillis());
    }

    /**
     * Cria um timestamp a partir de um Date.
     * 
     * @param date java.util.Date.
     */
    public Timestamp(java.util.Date date) {
        this(date.getTime());
    }

    /**
     * Cria um timestamp a partir de suas partes.
     * 
     * @param year Ano.
     * @param month Mês (de 1 a 12).
     * @param day Dia.
     * @param hour Hora.
     * @param minute Minuto.
     * @param second Segundo.
     * @param millis Milissegundos.
     */
    public Timestamp(int year, int month, int day, int hour, int minute, int second, int millis) {
        super(0);
        Calendar cal = this.getCalendar();
        cal.set(year, month - 1, day, hour, minute, second);
        cal.set(Calendar.MILLISECOND, millis);
        this.updateTime();
    }

    /**
     * Cria um timestamp a partir de um Date (data sem hora) e de um Time (hora/min/seg).
     * 
     * @param data Date.
     * @param time Time.
     */
    public Timestamp(Date data, Time time) {
        this(data.getYear(), data.getMonth(), data.getDay(), time.getHours(), time.getMinutes(), time.getSeconds(), 0);
    }

    /**
     * Soma dias ao timestamp.
     * 
     * @param amount Número de dias.
     * @return Timestamp atualizado.
     */
    public Timestamp addDays(int amount) {
        return this.addField(Calendar.DATE, amount);
    }

    /**
     * Soma meses ao timestamp.
     * 
     * @param amount Número de meses.
     * @return Timestamp atualizado.
     */
    public Timestamp addMonth(int amount) {
        return this.addField(Calendar.MONTH, amount);
    }

    /**
     * Soma anos ao timestamp.
     * 
     * @param amount Número de anos.
     * @return Timestamp atualizado.
     */
    public Timestamp addYears(int amount) {
        return this.addField(Calendar.YEAR, amount);
    }

    /**
     * Soma horas ao timestamp.
     * 
     * @param amount Número de horas.
     * @return Timestamp atualizado.
     */
    public Timestamp addHours(int amount) {
        return this.addField(Calendar.HOUR_OF_DAY, amount);
    }

    /**
     * Soma minutos ao timestamp.
     * 
     * @param amount Número de minutos.
     * @return Timestamp atualizado.
     */
    public Timestamp addMinutes(int amount) {
        return this.addField(Calendar.MINUTE, amount);
    }

    /**
     * Soma segundos ao timestamp.
     * 
     * @param amount Número de segundos.
     * @return Timestamp atualizado.
     */
    public Timestamp addSeconds(int amount) {
        return this.addField(Calendar.SECOND, amount);
    }

    /**
     * Soma meses ao milissegundos.
     * 
     * @param amount Número de milissegundos.
     * @return Timestamp atualizado.
     */
    public Timestamp addMillisconds(int amount) {
        return this.addField(Calendar.MILLISECOND, amount);
    }

    /**
     * Soma o valor informado a um campo específico do timestamp.
     * 
     * @param field Campo que vai ter o valor somado.
     * @param amount Valor a ser somado.
     * @return Timestamp atualizado.
     * @see java.util.Calendar#add(int, int)
     */
    private Timestamp addField(int field, int amount) {
        Calendar cal = this.getAndSetCalendar();
        cal.add(field, amount);
        this.updateTime();
        return this;
    }

    /**
     * Retorna o dia do timestamp.
     * 
     * @return Dia do timestamp.
     * @see java.util.Date#getDay()
     */
    @Override
    public int getDay() {
        return this.getAndSetCalendar().get(Calendar.DATE);
    }

    /**
     * Retorna o mês do timestamp (de 1 a 12).
     * 
     * @return Mês do timestamp (de 1 a 12).
     */
    @Override
    public int getMonth() {
        // Soma 1 ao mes (entre 1 e 12)
        return this.getAndSetCalendar().get(Calendar.MONTH) + 1;
    }

    /**
     * Retorna o ano do timestamp.
     * 
     * @return Ano do timestamp.
     * @see java.util.Date#getYear()
     */
    @Override
    public int getYear() {
        return this.getAndSetCalendar().get(Calendar.YEAR);
    }

    /**
     * Retorna a hora do timestamp.
     * 
     * @return Hora do timestamp.
     * @see java.util.Date#getHours()
     */
    @Override
    public int getHours() {
        return this.getAndSetCalendar().get(Calendar.HOUR_OF_DAY);
    }

    /**
     * Retorna o minuto do timestamp.
     * 
     * @return Minuto do timestamp.
     * @see java.util.Date#getMinutes()
     */
    @Override
    public int getMinutes() {
        return this.getAndSetCalendar().get(Calendar.MINUTE);
    }

    /**
     * Retorna o segundo do timestamp.
     * 
     * @return Segundo do timestamp.
     * @see java.util.Date#getSeconds()
     */
    @Override
    public int getSeconds() {
        return this.getAndSetCalendar().get(Calendar.SECOND);
    }

    /**
     * Retorna o milissegundo do timestamp.
     * 
     * @return Milissegundo do timestamp.
     */
    public int getMilliseconds() {
        return this.getAndSetCalendar().get(Calendar.MILLISECOND);
    }

    /**
     * Retorna o clone deste timestamp.
     * 
     * @return Clone deste timestamp.
     */
    @Override
    public Object clone() {
        return new Timestamp(this.getTime());
    }

    /**
     * Devolve a um calendar representando essa data.
     * 
     * @return um calendar representando essa data
     */
    public Calendar asCalendar() {
        Calendar ret = Calendar.getInstance();
        ret.setTime(this);
        return ret;
    }

    /**
     * Retorna o Calendar interno deste timestamp.
     * 
     * @return Calendar interno deste timestamp.
     */
    private Calendar getCalendar() {
        if (this.calendar == null) {
            this.calendar = Calendar.getInstance();
        }
        return this.calendar;
    }

    /**
     * Define e retorna o Calendar interno deste timestamp.
     * 
     * @return Calendar interno deste timestamp.
     */
    private Calendar getAndSetCalendar() {
        Calendar cal = this.getCalendar();
        cal.setTime(this);
        return cal;
    }

    /**
     * Atualiza este timestamp a partir do Calendar interno.
     */
    private void updateTime() {
        Calendar cal = this.getCalendar();
        this.setTime(cal.getTimeInMillis());
    }
}
